﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;

namespace AwesomeEnginePC.Audio
{
    public class AudioManager
    {
        ContentManager _cm;

        /* rules about simple:
         * -only one song can be playing
         * -if trying to play a song while something is playing, the previous will be stopped
         * -sound effects will be spawned and then killed when done playing
         */

        public List<AudioSong> LoadedSongs { get; protected set; }
        public List<AudioSoundEffect> LoadedSoundEffects { get; protected set; }

        public AudioManager(ContentManager cm)
        {
            _cm = cm;
            LoadedSongs = new List<AudioSong>();
            LoadedSoundEffects = new List<AudioSoundEffect>();
        }

        /// <summary>
        /// Stops all songs
        /// </summary>
        public void StopAllSongs()
        {
            MediaPlayer.Stop();
        }

        public void StopAllSoundEffects()
        {
            foreach (AudioSoundEffect soundEffect in LoadedSoundEffects)
            {
                soundEffect.Stop();
            }
        }

        public AudioSong GetSongByName(string assetName)
        {
            return LoadedSongs.Where((x) => x.ComponentName == assetName).Single();
        }

        public AudioSoundEffect GetSoundEffectByName(string assetName)
        {
            return LoadedSoundEffects.Where((x) => x.ComponentName == assetName).Single();
        }

        public void LoadSong(string assetName)
        {
            LoadedSongs.Add(new AudioSong(_cm, assetName));
        }

        public void LoadSoundEffect(string assetName)
        {
            LoadedSoundEffects.Add(new AudioSoundEffect(_cm, assetName));
        }
    }

    public abstract class BaseAudioComponent
    {
        
        protected ContentManager cm;

        public string ComponentName { get; set; }

        public abstract void Play();
        public abstract void Stop();

        protected abstract void LoadAsset(string assetName);

        public BaseAudioComponent(ContentManager contentManager, string assetName)
        {
            cm = contentManager;
            ComponentName = assetName;
            LoadAsset(assetName);
        }

    }

    public class AudioSoundEffect : BaseAudioComponent
    {
        SoundEffect soundEffect;

        public List<SoundEffectInstance> Instances { get; private set; }

        public AudioSoundEffect(ContentManager contentManager, string assetName) : base(contentManager, assetName)
        {
            Instances = new List<SoundEffectInstance>();
        }

        /// <summary>
        /// Spawns an instance of the sound effect with default values and plays it.
        /// </summary>
        public override void Play()
        {
            Play(1f);
        }

        /// <summary>
        /// Spawns an instance of the sound effect with a given volume and plays it.
        /// </summary>
        /// <param name="volume">A float values between 0.0 (silence) and 1.0 (full volume).</param>
        public void Play(float volume)
        {
            Play(volume, 0f, 0f, false);
        }

        /// <summary>
        /// Spawns an instance of the sound effect with the given values and plays it.
        /// </summary>
        /// <param name="volume">A float value between 0.0 (silence) and 1.0 (full volume). 1.0 is default.</param>
        /// <param name="pan">A float value between -1.0 (full left) and 1.0 (full right). 0.0 is default.</param>
        /// <param name="pitch">A float value between -1.0 (down one octave) and 1.0 (up one octave). 0.0 is default.</param>
        /// <param name="isLooped">Tells if the instance will be looped when it is complete.</param>
        public void Play(float volume, float pan, float pitch, bool isLooped)
        {
            SoundEffectInstance instance = soundEffect.CreateInstance();
            instance.Volume = volume;
            instance.Pan = pan;
            instance.Pitch = pitch;
            instance.IsLooped = isLooped;
            instance.Play();

            Instances.Add(instance);
        }

        protected override void LoadAsset(string assetName)
        {
            soundEffect = cm.Load<SoundEffect>(assetName);
        }

        /// <summary>
        /// Stops all instances of this sound effect and clears the instance list.
        /// </summary>
        public override void Stop()
        {
            foreach (SoundEffectInstance instance in Instances)
            {
                if (!instance.IsDisposed)
                {
                    instance.Stop();
                    instance.Dispose();
                }
            }

            Instances.Clear();
        }

    }

    public class AudioSong : BaseAudioComponent
    {
        Song song;

        public AudioSong(ContentManager contentManager, string assetName) : base(contentManager, assetName) { }

        private Song GetActiveSong()
        {
            return MediaPlayer.Queue.ActiveSong;
        }

        public float Volume
        {
            get { return MediaPlayer.Volume; }
            set
            {
                if (value < 0.0f)
                    MediaPlayer.Volume = 0.0f;
                else if (value > 1.0f)
                    MediaPlayer.Volume = 1.0f;
                else
                    MediaPlayer.Volume = value;
            }
        }

        /// <summary>
        /// Plays the song. If the song is paused, will resume. If any other song is active, will stop the song and start playing this one.
        /// </summary>
        public override void Play()
        {
            if (song != GetActiveSong())
            {
                //different song is playing, stop it and play this one
                MediaPlayer.Stop();
                MediaPlayer.Play(song);
            }
            else
            {
                //this song is active, check if paused, and if true, resume
                if (MediaPlayer.State == MediaState.Paused)
                    MediaPlayer.Resume();
            }
        }

        /// <summary>
        /// Resumes the song if this is the active song and the media player is paused.
        /// </summary>
        public void Resume()
        {
            if (MediaPlayer.State != MediaState.Paused)
                return;

            if (song != GetActiveSong())
                return;

            MediaPlayer.Resume();
        }

        /// <summary>
        /// Pauses the song if the song is playing.
        /// </summary>
        public void Pause()
        {
            if (MediaPlayer.State != MediaState.Playing)
                return;

            if (song != GetActiveSong())
                return;

            MediaPlayer.Pause();
        }

        /// <summary>
        /// Stops the song if the song is playing.
        /// </summary>
        public override void Stop()
        {
            if (MediaPlayer.State == MediaState.Stopped)
                return;

            if (song != GetActiveSong())
                return;

            MediaPlayer.Stop();
        }

        protected override void LoadAsset(string assetName)
        {
            song = cm.Load<Song>(assetName);
        }
    }

}
